#include <windows.h>

#include "lib.h"

#include "crapconn.h"
#include "crapgame.h"
#include "crbet.h"
#include "crround.h"
#include "log.h"
#include "tbllim.h"

#include "stdncerr.h"
#include "badpacketmsgs.h"

#include "GameDBInterface.h"

#include "CRXMLTranslator.h"

CrapsConnection::CrapsConnection(Server* server, SOCKET client)
	: Connection(server, client)
{	
    game      = NULL;
	balance   = 0;	
    loggedOn  = false;
    m_eGameId = INVALID_ID;

	casinoSpecific = NULL;
    m_pDBInterface = NULL;

    // kharmon.  11-16-06.  #16921.
	msglen = 0;
	memset(msgbuf,0,sizeof(msgbuf));    

	m_iForcedRollValues = NULL;
}

CrapsConnection::~CrapsConnection()
{
	if (game)
    {
		game->save();
    }

	if (m_iForcedRollValues)
	{
		delete m_iForcedRollValues;
	}

	delete game;
	game = NULL;

    delete m_pDBInterface;
    m_pDBInterface = NULL;

	// CSN-7075: New Authentication
	trackLogoutSession();

	removeCasinoSpecific();

	loggedOn = false;
}


void
CrapsConnection::msgStart()
{
	msglen = 0;
}

void
CrapsConnection::msgAdd(uint8 Byte)
{
	msgbuf[msglen++] = Byte;
}

void
CrapsConnection::msgAdd(int32 SignedLong)
{
	msgbuf[msglen++] = (uint8)((SignedLong) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 8) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 16) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 24) & 0xff);
}

uint8*
CrapsConnection::msgGetBuf()
{
	return msgbuf + msglen;
}

void
CrapsConnection::msgReleaseBuf(int32 Amt)
{
	msglen += Amt;
}

void
CrapsConnection::msgEnd()
{
	msglen = encode(msgbuf, obuf + oused + 2, msglen);
	msglen += 2;	// include msg len bytes (2) in length
	obuf[oused] = (uint8)(msglen & 0xff);
	obuf[oused+1] = (uint8)((msglen >> 8) & 0xff);
	oused += msglen;
}

void
CrapsConnection::sendStopMsg()
{
	msgStart();
	msgAdd((uint8)'!');
	msgEnd();
}

int32
CrapsConnection::msgRemove(uint8 *Src, int32 *pResult)
{
	*pResult = (int32)(
		(Src[0] & 0xFF) |
		((Src[1] << 8) & 0xFF00) |
		((Src[2] << 16) & 0xFF0000) |
		((Src[3] << 24) & 0xFF000000));

	return 4;
}

int32
CrapsConnection::msgRemove(uint8 *Src, uint8 *pResult)
{
	*pResult = *Src;

	return 1;
}

bool8
CrapsConnection::getResponse()
{
	int32 ieat = 0, remain = iused, len, srclen;
	uint8* src = ibuf;
	int32 err = 0;

	// [AO 2007-10-01] We can send just one reply for certain repeating messages.
	// We're not handling partial buffers and incomplete messages but that's OK.
	char last_message = 0;

	while (remain >= 2)
	{
		srclen = src[0] + (src[1] << 8);

		if (remain < srclen)
			break;

		// Handle an invalidly formatted message of length zero.
		if (srclen <= 2)        // kharmon.  11-16-06.  #16921.  
		{
			ieat += 2;
			remain -= 2;
			src += 2;
			continue;
		}

        // kharmon.  11-16-06.  #16921.
	    msglen = 0;
	    memset(msgbuf,0,sizeof(msgbuf));

        if(srclen>sizeof(msgbuf))
        {
            // hmmmm...this message is longer than our msgbuf...no good.
			ieat = iused;
			break;
        }

		len = decode(src + 2, msgbuf, srclen - 1);
		ieat += srclen;
		remain -= srclen;
		src += srclen;

		switch (msgbuf[0])
		{

			case 'L':
				if (!parseAnsiCasinoLogin(&err))
				{
					msgStart();
					msgAdd((uint8)'E');
					msgAdd((uint8)(err & 0xff));
					msgEnd();
					break;
				}

				handleLogin(len);
				break;

			case 'M':
			{
				switch (msgbuf[1])
				{
					case 'L':
					{
						if (!parseUnicodeCasinoLogin(&err))
						{
							msgStart();
							msgAdd((uint8)'E');
							msgAdd((uint8)(err & 0xff));
							msgEnd();
							break;
						}

						handleLogin(len);
						break;
					}

					default:

						trackaddBadPacket(GETREPONSE_DEFAULT_CASE, msgbuf[0], (char*)msgbuf,len);

						Log::msgWarning("CrapsConnection::getResponse:  Unknown input"
							" (0x%02x)  ", msgbuf[0]);
						msgStart();
						msgAdd((uint8)'N');
						msgEnd();
						break;
				}
				break;
			}

			case 'm':  // Balance Request
				// balance Broadcast System (BBS)
				// client needs fresh balance to synchronize balance display
				if (isServerStopping())
				{
					sendStopMsg();
					break;
				}

				// [AO 2007-10-01] Ignore repeating messages
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (msgbuf[0] == last_message || !ValidateSession())
					break;

				//rule1: gotta be logged on
				if( loggedOn )
				{
					int32 balance, compBalance, bonusBalance;
				
					if( m_pDBInterface->trackGetBalance(&balance,&compBalance, &bonusBalance) )
					{
						msgStart();
						msgAdd((uint8)'m');
						msgAdd(balance);
						msgEnd();
					}
				}
				break;

			default:
            {
			    if (!loggedOn)
			    {
				    Log::msgWarning("Craps Roll: not logged on...invalid bet  ");

					trackaddBadPacket(TRY_ROLL_NOT_LOGGED_ON, msgbuf[0], (char*)msgbuf,len);

				    msgStart();
				    msgAdd((uint8)'B');
				    msgEnd();

				    break;
			    }

			    if (isServerStopping())
			    {
				    sendStopMsg();
				    break;
			    }

                if (!game)
                {
                    trackaddBadPacket(TRY_DBLUPDENY_NOT_LOGGED_ON, msgbuf[0], (char*) msgbuf, len);

				    msgStart();
				    msgAdd((uint8)'N');
				    msgEnd();

                    break;
                }

				// [AO 2008-01-16] HA: verify the user is still logged on
				if (!ValidateSession())
					break;

                // kharmon.  06-19-06.  #3077.
                // Let the game object try to process the message
                if(!game->handleMessage(msgbuf, len))
                {
                    // Hmmmm.  The game object didn't recognize the message either.
				    trackaddBadPacket(GETREPONSE_DEFAULT_CASE, msgbuf[0], (char*)msgbuf,len);

					Log::msgWarning("CrapsConnection::getResponse:  Unknown input"
						" (0x%02x)  ", msgbuf[0]);
				    msgStart();
				    msgAdd((uint8)'N');
				    msgEnd();
                }

				break;
            }
		}

		last_message = msgbuf[0]; // [AO 2007-10-01] Handle repeating messages
	}

	if (iused <= ieat) // [AO 2007-10-01] Avoid buffer overflow
		iused = 0;
	else
	{
		MoveMemory(ibuf, ibuf + ieat, iused - ieat);
		iused -= ieat;
	}

	return (bool8)(oused != 0);
}

void
CrapsConnection::handleLogin(int32 len)
{
	int32 err = 0;
	bool8 saved = false;
	int32 balance, compBalance, bonusBalance;

	if (isServerStopping())
	{
		sendStopMsg();
		return;
	}

	if (!loggedOn)
	{
		if (!m_pDBInterface->trackLoadOptions())
		{
			Log::msgError("Craps Login: failed to get limits ");

			msgStart();
			msgAdd((uint8)'N');
			msgEnd();
			return;
		}

		if (!m_pDBInterface->trackHasSavedGame(game, &err) && err)
		{
			Log::msgError("Craps Login: couldn't re-create saved"
				" game ");

			msgStart();
			msgAdd((uint8)'E');
			msgAdd((uint8)(err & 0xff));
			msgEnd();
			return;
		}

		if( !m_pDBInterface->trackGetBalance(&balance,&compBalance, &bonusBalance) )
		{
			msgStart();
			msgAdd((uint8)'N');
			msgEnd();
			return;
		}

		
		// if there wasn't a saved game, create a new game
		if (game == NULL)
		{            
            game = DiceGame::CreateGame(this);  // kharmon.  06-19-06.  #3077.  This call will return the correct type of game object.

			if (!game)
			{
				Log::msgError("Craps Login:  can't create game ");

				delete game;
				game = (CrapsGame*)0;

				msgStart();
				msgAdd((uint8)'N');
				msgEnd();
				return;
			}
		}
		else
		{
			saved = true;
		}

		loggedOn = true;


		msgStart();

		msgAdd((uint8)'L');
		msgAdd(balance);

		game->send(saved);

		msgEnd();

	}
	else
	{
		Log::msgWarning("Craps Login: attempt to re-log on  ");

		trackaddBadPacket(TRY_LOGON_WHEN_LOGGED_ON,msgbuf[0],(char*)msgbuf,len);

		msgStart();
		msgAdd((uint8)'N');
		msgEnd();
	}
}

void
CrapsConnection::allocateXMLTranslator( XMLTranslator** target )
{
	*target = new CRXMLTranslator( getServer(), this );
}

// [AO 2008-01-16] Refactoring for HA
bool CrapsConnection::ValidateSession()
{
	int32 session_check = trackValidateSession();
	if (!session_check) // session is valid
		return true;

	// [AO 2008-01-22] Additional case from RTG 6.6.0 code drop
	if (session_check == TrackSQL::NormalLogout)
		session_check = LOGINERR_NOTLOGGEDIN;

	msgStart();
	msgAdd(static_cast<uint8>('N'));
	msgAdd(static_cast<int32>(session_check));
	msgEnd();
	return false;
}
